<?php
/**
 * Event
 * 
 * @category	Event
 * @package		dayscript.event
 * @subpackage	
 * @author		Nelson Daza <ndaza@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license		
 * @version		1.0
 * @version		$Revision: 0 $
 * @filesource	
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		
 * @since		1.0
 * @modifiedby	$LastChangedBy: Nelson Daza $
 * @modified	$Date: 2009-09-24 $
 */

namespace dayscript\event;

/**
 * Event
 * 
 * The Event class is used as the base class for the creation of Event objects, which are passed as parameters to event listeners when an event occurs. 
 *
 * @category	Event
 * @package		dayscript.event
 * @subpackage	
 * @author		Nelson Daza <ndaza@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license		
 * @version		1.0
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		
 * @since		1.0
 * @modifiedby	$LastChangedBy: Nelson Daza $
 * @modified	$Date: 2009-09-24 $
 * @todo add target currentTarget eventDispacher
 */
class Event	{
	/**
	 * Whether an event is a bubbling event.
	 * 
	 * When an event occurs, it moves through the three phases of the event flow: 
	 * 1. The capture phase, which flows from the top of the listeners list hierarchy to the object just before the target object; 
	 * 2. The target phase, which comprises the target object; 
	 * 3. The bubbling phase, which flows from the object subsequent to the target object back up the list hierarchy.
	 * 
	 * Some events, such as the activate and unload events, do not have a bubbling phase. 
	 * 
	 * @var boolean $bubbles
	 */
	protected $bubbles = false;
	/**
	 * Whether the behavior associated with the event can be prevented.
	 * 
	 * @var boolean $cancelable
	 */
	protected $cancelable = false;	
	/**
	 * The object that is actively processing the Event object with an event listener.
	 * 
	 * @var mixed $currentTarget
	 */
	protected $currentTarget = null;	
	/**
	 * The current phase in the event flow. This property can contain the following numeric values:
	 * 
	 * The capture phase	(Event::PHASE_CAPTURING).
	 * The target phase		(Event::PHASE_TARGET).
	 * The bubbling phase	(Event::PHASE_BUBBLING).
	 * 
	 * @var int $phase
	 */
	protected $phase = self::PHASE_NONE;
	/**
	 * The event target.
	 * 
	 * @var mixed $currentTarget
	 */
	protected $target = null;	
	/**
	 * The type of event. The type is case-sensitive.
	 * 
	 * @var string $type
	 */
	protected $type = null;
	/**
	 * Creation trace including file,line.
	 * 
	 * @var array $creationTrace
	 * @todo
	 */
	protected $creationTrace = null;
	/**
	 * Default action for this event.
	 * 
	 * @var function $defaultAction
	 * @todo
	 */
	protected $defaultAction = null;
	/**
	 * Whether the preventDefault( ) method has been called on the event and the execution of "defaultAction" has been canceled.
	 * 
	 * @var boolean $defaultPrevented
	 */
	protected $defaultPrevented = false;
	/**
	 * Whether the stopPropagation( ) method has been called on the event.
	 * 
	 * @var boolean $stopped
	 */
	protected $stopped = false;
	/**#@+
	 * Defines the fase types.
	 * @var int
	 */
	const PHASE_NONE			= 0;
	const PHASE_CAPTURING		= 1;
	const PHASE_TARGET			= 2;
	const PHASE_BUBBLING		= 4;
	/**#@-*/
	/**#@+
	 * Defines the value of the type property of an activate event object.
	 * @var string
	 */
	const LOAD			= 'load';
	const UNLOAD		= 'unload';
	
	const INIT			= 'init';
	const OPEN			= 'open';
	const CLOSING		= 'closing';
	const CLOSE			= 'close';
	const SHUTDOWN		= 'shutdown';
	
	const START			= 'start';
	const END			= 'end';
	
	const RENDER_START	= 'renderStart';
	const RENDER		= 'render';
	const RENDER_END	= 'renderEnd';
	
	const ENTERING		= 'entering';
	const EXITING		= 'exiting';
	
	const CONNECT		= 'connect';
	const DISCONNECT	= 'disconnect';
	
	const ACTIVATE		= 'activate';
	const DEACTIVATE	= 'deactivate';
	
	const ADDED			= 'added';
	const REMOVED		= 'removed';
	
	const COMPLETE		= 'complete';
	
	const CANCEL		= 'cancel';
	const CHANGE		= 'change';
	const RESIZE		= 'resize';
	const SELECT		= 'select';

	const MODULE_LOAD	= 'moduleLoad';
	const MODULE_START	= 'moduleStart';
	const MODULE_END	= 'moduleEnd';
	const MODULE_UNLOAD	= 'moduleUnload';
	
	const MODULE_RENDER_START	= 'moduleRenderStart';
	const MODULE_RENDER			= 'moduleRender';
	const MODULE_RENDER_END		= 'moduleRenderEnd';
	
	const USER_IDLE		= 'idle';
	const USER_PRESENT	= 'present';
	/**#@-*/
	/**
	 * Event constructor, creates an Event object to pass as a parameter to event listeners.
	 *  
	 * @param string $type
	 * @param boolean $bubbles
	 * @param boolean $cancelable
	 * @param callable $defaultAction
	 * @return \dayscript\event\Event $event
	 */
	public function __construct( $type, $bubbles = false, $cancelable = false, $defaultAction = null )	{
		$this->creationTrace = \dayscript\ErrorManager::getBacktrace( 0, 2 );
		if( isset( $this->creationTrace[0] ) )
			$this->creationTrace[0]['time'] = \microtime( true );
		if( isset( $this->creationTrace[1] ) )
			$this->creationTrace[1]['time'] = \microtime( true );
		
		/*
		$stack = \dayscript\ErrorManager::getBacktrace( 0, 2 );
		$this->creationTrace = array( );
		$this->creationTrace[0]['file'] = ( $stack[0]['file'] ? '...' . \substr( $stack[0]['file'], -35 ) : '\\' . $stack[0]['class'] . $stack[0]['type'] . $stack[0]['function'] );
		$this->creationTrace[0]['line'] = $stack[0]['line'];
		$this->creationTrace[0]['time'] = \microtime( true ); 
		if( isset( $stack[1] ) )	{
			$this->creationTrace[1]['file'] = ( $stack[1]['file'] ? '...' . \substr( $stack[1]['file'], -35 ) : '\\' . $stack[1]['class'] . $stack[1]['type'] . $stack[1]['function'] );
			$this->creationTrace[1]['line'] = $stack[1]['line'];
			$this->creationTrace[1]['time'] = \microtime( true ); 
		}
		*/
		$this->type = (string)$type;
		$this->bubbles = (boolean)$bubbles;
		$this->cancelable = (boolean)$cancelable;
		if( $defaultAction )	{
			if( !\is_callable( $defaultAction ) )
				throw new \InvalidArgumentException( 'No valid callable default action for this Event.', \E_USER_ERROR );
			$this->defaultAction = $defaultAction; 
		}
	}
	/**
	 * Whether the events bubbles or not.
	 * @return boolean $bubbles
	 */
	public function isBubbles( )	{
		return $this->bubbles;
	}
	/**
	 * Whether the events is cancelable or not.
	 * @return boolean $cancelable
	 */
	public function isCancelable( )	{
		return $this->cancelable;
	}
	/**
	 * Whether the preventDefault( ) method has NOT been called on the event.
	 * @return boolean $defaultPrevent
	 */
	public function isDefaultPrevented( )	{
		return $this->defaultPrevented;
	}
	/**
	 * Whether the stopPropagation( ) method has been called on the event.
	 * @return boolean $stopped
	 */
	public function isStopped( )	{
		return $this->stopped;
	}
	/**
	 * Returns the object that is actively processing the Event.
	 * @return mixed $currentTarget
	 */
	public function getCurrentTarget( )	{
		return $this->currentTarget;
	}
	/**
	 * Sets the object that is actively processing the Event.
	 * @return mixed $currentTarget
	 */
	public function setCurrentTarget( $target )	{
		return $this->currentTarget = $target;
	}
	/**
	 * Returns the current event phase of this Event.
	 * @return int $target
	 */
	public function getPhase( )	{
		return $this->phase;
	}
	/**
	 * Sets the current event phase of this Event.
	 * @param int $phase
	 */
	public function setPhase( $phase )	{
		if( $phase > $this->phase );
			$this->phase = $phase;
	}
	/**
	 * Returns the object target of this Event.
	 * @return mixed $target
	 */
	public function getTarget( )	{
		return $this->target;
	}
	/**
	 * Sets the object target of this Event.
	 * @param mixed $target 
	 */
	public function setTarget( $target )	{
		$this->target = $target;
	}
	/**
	 * Returns the type of this Event.
	 * @return mixed $type
	 */
	public function getType( )	{
		return $this->type;
	}
	/**
	 * Returns the trace of Event creation.
	 * @return array $creationTrace
	 */
	public function getCreationTrace( )	{
		return $this->creationTrace;
	}
	/**
	 * Returns the default action for this Event.
	 * @return array $defaultAction
	 */
	public function getDefaultAction( )	{
		return $this->defaultAction;
	}
	/**
	 * Cancels an event's default behavior if that behavior can be canceled. 
	 */
	public function preventDefault( )	{
		$this->defaultPrevented = true;
	}
	/**
	 * Prevents processing of any event listeners in the event flow.
	 * 
	 * Additional calls to this method have no effect.
	 * This method can be called in any phase of the event flow.
	 * Note: This method does not cancel the behavior associated with this event; see preventDefault() for that functionality.
	 */
	public function stopPropagation( )	{
		if( $this->cancelable )	{
			\dayscript\log\LogManager::log( 'EVENT STOP propagation: "' . \strip_tags( \dayscript\util\Debug::dumpVar( $this->target, true ) ) . '"::' . $this->getType( ) . ' to "' . \strip_tags( \dayscript\util\Debug::dumpVar( $this->currentTarget, true ) ) . '".', \dayscript\log\LogManager::LOG_USER_INFO );
			$this->stopped = true;
		}
	}
	/**
	 * Returns a string containing all the properties of the Event object. The string is in the following format: [Event type=value bubbles=value cancelable=value]
	 * @return string A string containing all the properties of the Event object.
	 */
	public function __toString( )	{
		return sprintf( '[%s type=%s bubbles=%s cancelable=%s dispatcherTarget=%s currentTarget=%s]', __CLASS__, $this->type, ( $this->bubbles ? 'true' : 'false' ), ( $this->cancelable ? 'true' : 'false' ), ( $this->target ? ( \is_object( $this->target ) ? \get_class( $this->target ) : $this->target ) : 'null' ), ( $this->currentTarget ? ( \is_object( $this->currentTarget ) ? \get_class( $this->currentTarget ) : $this->currentTarget ) : 'null' ) );
	}
}
